﻿using System;
using System.Collections.Generic;
using System.Linq;
using BitsOfStuff.MyActions;

namespace BitsOfStuff
{
    /// <summary>
    /// Function for nodle in vector tree
    /// </summary>
    [Serializable]
    public class MyUserProfileNodle : ICloneable
    {
        //Value of this noodle
        private readonly int _value;
        /// <summary>
        /// inform us , if this noodle is end leaf of some char
        /// </summary>
        public bool IsLeaf;

        //User profile belong to this tree, only root have it
        private readonly UserProfile _belongUserProfil;

        private UserProfile getUserProfile()
        {
            return _belongUserProfil;
        }

        //all noodle have some action , whitch can be promt to be made
        private List<MyAction> _action;

        private List<MyAction> _siblingsActions;

        //Dictonary of children Noodle , key is value( sequence ) and value is childrenNodle
        private readonly Dictionary<int, MyUserProfileNodle> _childrenNode;

        //rootNoodle , noodle whitch have references to profile ( profile have references to local resorces)
        private readonly MyUserProfileNodle _rootNodle;

        //Unuset Dictonary jet, have parent Nodes

        //Constructor for nonRootNodes that do something , its call constructor for noodle that do nothing
        // and action is added
        /// <summary>
        /// Constructor for nonRootNodes that do something , its call constructor for noodle that do nothing
        /// and action is added
        /// </summary>
        /// <param name="parent">Parent noodle</param>
        /// <param name="value">value of noodle</param>
        /// <param name="action">action of noodle</param>
        /// <param name="rootNodle">root noodle of vector tree</param>
        public MyUserProfileNodle(MyUserProfileNodle parent, int value, MyAction action, MyUserProfileNodle rootNodle)
            : this(parent, value, rootNodle)
        {
            _action.Add(action);
        }

        //Constructor for nonRoot Nodes , it have as parametr Noodle whitch is its parent ,
        // its value and root nodle for tree , this is constructor for noodle whitch do nothing
        /// <summary>
        ///Constructor for nonRoot Nodes , it have as parametr Noodle whitch is its parent ,
        /// its value and root nodle for tree , this is constructor for noodle whitch do nothing
        /// </summary>
        /// <param name="parent">Parent noodle</param>
        /// <param name="value">value of noodle</param>
        /// <param name="rootNodle">root noodle of vector tree</param>
        public MyUserProfileNodle(MyUserProfileNodle parent, int value, MyUserProfileNodle rootNodle)
        {
            _childrenNode = new Dictionary<int, MyUserProfileNodle>();
            _value = value;
            _action = new List<MyAction> { new MyDoNothingAction() };
            _rootNodle = rootNodle;
            _siblingsActions = new List<MyAction>();
        }

        //Constructor for root Nodle , its only have user profile as parameter, becouse it havent got any
        //Parent and any value
        /// <summary>
        /// Constructor for root Nodle , its only have user profile as parameter, becouse it havent got any
        /// Parent and any value
        /// </summary>
        /// <param name="userProfile">user profile that holds this tree</param>
        public MyUserProfileNodle(UserProfile userProfile)
        {
            _value = new int();
            _childrenNode = new Dictionary<int, MyUserProfileNodle>();
            _belongUserProfil = userProfile;
            _rootNodle = this;
            _action = new List<MyAction>() { new MyDoNothingAction() };
            _siblingsActions = new List<MyAction>();
        }

        //set action for curent noodle
        /// <summary>
        /// set action for curent noodle
        /// </summary>
        /// <param name="action"></param>
        public void setAction(MyAction action)
        {
            _action.Add(action);
        }

        //function for adding children , its return new added children ,
        // for adding rest of sentence to tree
        /// <summary>
        ///  function for adding children , its return new added children ,
        /// for adding rest of sentence to tree
        /// </summary>
        /// <param name="children">new children</param>
        /// <returns>newed createrd children</returns>
        public MyUserProfileNodle addChildren(MyUserProfileNodle children)
        {
            if (_childrenNode.ContainsKey(children._value))
            {
                MyAction hightPriorityAction = MyAction.getHighPriorityAction(children._action[0],
                                                                        _childrenNode[children._value]._action[0]);
                _childrenNode[children._value].setAction(hightPriorityAction);
                return _childrenNode[children._value];
            }
            //else
            _childrenNode.Add(children.getValue(), children);
            return children;
        }

        //function for adding children only by its value
        /// <summary>
        /// Function for adding children only by its value
        /// </summary>
        /// <param name="value">value of children</param>
        /// <returns>new nodle whit this children</returns>
        public MyUserProfileNodle addChildren(int value)
        {
            if (_childrenNode.ContainsKey(value))
                return _childrenNode[value];
            var newChildren = new MyUserProfileNodle(this, value, _rootNodle);
            _childrenNode.Add(value, newChildren);
            return newChildren;
        }

        //Function only for geting value of this noodle
        /// <summary>
        /// Function only for geting value of this noodle
        /// </summary>
        /// <returns>value of this nodle</returns>
        public int getValue()
        {
            return _value;
        }

        /// <summary>
        /// Function for returning children by specified childrenValue
        /// </summary>
        /// <param name="childrenValue">value of childre</param>
        /// <returns>children whith value</returns>
        public MyUserProfileNodle getChildren(int childrenValue)
        {
            MyUserProfileNodle result;
            _childrenNode.TryGetValue(childrenValue, out result);
            return result;
        }

        //Function to add seqenc of int to itself and his ancestors
        /// <summary>
        /// Function to add seqenc of int to itself and his ancestors
        /// </summary>
        /// <param name="sequence">sequence to store</param>
        /// <param name="action">action to store</param>
        public void addSequence(List<int> sequence, MyAction action)
        {
            addAction(action, sequence);
            if (sequence.Count > 1)
            {
                addSequenceNodle(sequence, action);
            }
            else // adding leaf
            {
                action.setFinal();
                addSequenceLeaf(sequence, action);
            }
        }

        private void addSequenceNodle(List<int> sequence, MyAction action)
        {
            int firsChildrenValue = sequence[0];
            sequence.Remove(sequence[0]);
            if (IsLeaf && sequence[0] == -1)
            {
                var decisionAction = new MyDecisionAction((MyAction)_action.LastOrDefault().Clone());
                _action = new List<MyAction>() { decisionAction };
                _rootNodle._belongUserProfil.addDecisionAction(decisionAction, action);
            }
            addChildren(firsChildrenValue).addSequence(sequence, action);
        }

        private void addSequenceLeaf(List<int> sequence, MyAction action)
        {
            // adding new brench
            if (_childrenNode.Count == 0)
                addChildren(new MyUserProfileNodle(this, sequence[0], (MyAction)action.Clone(), _rootNodle));
            else
            {
                if (_childrenNode.ContainsKey(sequence[0]))
                {
                    var children = _childrenNode[sequence[0]];
                    if (children.IsLeaf && !children.getAction().Contains(action))
                    {
                        children.addActionLeaf((MyAction)action.Clone());
                        return;
                    }
                }
                MyAction directAction = null;
                isDirect(ref directAction);
                if (directAction != null && directAction.CompareTo(action) == 0 && !_childrenNode.ContainsKey(-1))
                {
                    addChildren(new MyUserProfileNodle(this, sequence[0], (MyAction)action.Clone(), _rootNodle));
                }
                else
                {
                    // adding action on someone branch
                    addChildren(new MyUserProfileNodle(this,
                                                       sequence[0],
                                                       new MyDecisionAction((MyAction)action.Clone()),
                                                       _rootNodle));
                }
            }
            _childrenNode[sequence[0]].IsLeaf = true;
        }

        private List<MyAction> getAction()
        {
            return _action;
        }

        //Function to return action from last ancestor from sequenc finding.
        /// <summary>
        /// Function to return action from last ancestor from sequenc finding.
        /// </summary>
        /// <param name="sequence">sequence for find</param>
        /// <param name="profileCurentNoodle">starting noodle</param>
        /// <returns>found actions</returns>
        public List<MyAction> findSequence(List<int> sequence, ref MyUserProfileNodle profileCurentNoodle)
        {
            if (_action != null && sequence.Count == 0)
            {
                profileCurentNoodle = this;
                return _action;
            }

            if (_childrenNode.ContainsKey(sequence[0]))
            {
                int child = sequence[0];
                sequence.RemoveAt(0);
                return _childrenNode[child].findSequence(sequence, ref profileCurentNoodle);
            }

            if (_action != null && _action.Count > 0 && _action[0].isDoingSomething())
            {
                return new List<MyAction>() { _action[0] };
            }

            return _siblingsActions;
        }

        public object Clone()
        {
            return MemberwiseClone();
        }

        /// <summary>
        /// Function that find if all children have same and only action
        /// </summary>
        /// <param name="directAction">output action , that have all children </param>
        /// <returns>status if noodle is direct</returns>
        public bool isDirect(ref MyAction directAction)
        {
            if (_childrenNode.Count == 1)
            {
                _childrenNode[_childrenNode.Keys.FirstOrDefault()].isDirect(ref directAction);
            }
            else
            {
                if (_childrenNode.Count == 0)
                {
                    directAction = _action[0];
                    return true;
                }
                return false;
            }
            return false;
        }

        private void addAction(MyAction action, List<int> sequence)
        {
            bool singleStroke = true;

            for (int i = 0; i < sequence.Count - 1; i++)
            {
                if (sequence[i] == -1)
                {
                    singleStroke = false;
                    break;
                }
            }

            //Add action to sibblings Action
            if (singleStroke && !_siblingsActions.Exists(sa => sa.Equals(action)))
                _siblingsActions.Add((MyAction)action.Clone());

            // If nodle dont have childre, so new sequence will be its only path , make
            //his action same as sequence action

            if (_childrenNode.Count == 0)
            {
                List<MyAction> tmp = _action.Where(myAction => myAction is MyShowAction).ToList();
                _action = tmp;
                var lastOrDefault = _action.LastOrDefault();
                if (lastOrDefault == null || !lastOrDefault.isDoingSomething())
                    _action = new List<MyAction> { (MyAction)action.Clone() };
            }
            else
            {
                if (IsLeaf)
                    return;

                int actionCompare = action.CompareTo(_action);

                switch (actionCompare)
                {
                    case -1:
                        _action = new List<MyAction> { new MyDoNothingAction() };
                        break;
                    case 0:
                        break;
                    case 1:
                        _action = new List<MyAction> { new MyDoNothingAction() };
                        break;
                }
                // MyAction directAction = null;
                // if (isDirect(ref directAction))
                // {
                //     if (action.CompareTo(directAction) == 0)
                //          _action = action;
                //  }
            }
        }

        private void addActionLeaf(MyAction action)
        {
            if (!_siblingsActions.Exists(sa => sa.Equals(action)))
                _siblingsActions.Add(action);
            if (!_action.Exists(ac => ac.Equals(action)))
                _action.Add(action);
        }

        /// <summary>
        /// function from remooving char from vector tree
        /// </summary>
        /// <param name="c">char to remove</param>
        public void removeAction(char c)
        {
            _siblingsActions.RemoveAll(a => (a is MyShowAction) && ((MyShowAction)a).getChar() == c);
            if (_siblingsActions.Count == 0)
                _siblingsActions = new List<MyAction>();
            _action.RemoveAll(a => (a is MyShowAction) && ((MyShowAction)a).getChar() == c);
            if (_action.Count == 0)
                _action = new List<MyAction>();
            foreach (MyUserProfileNodle nodle in _childrenNode.Values)
            {
                nodle.removeAction(c);
            }
        }
    }
}